<?php
/**
 *
 * Data module for shop users
 *
 * @package	VirtueMart
 * @subpackage User
 * @author Oscar van Eijk
 * @author Max Milbers
 * @author	RickG
 * @link http://www.virtuemart.net
 * @copyright Copyright (c) 2004 - 2010 VirtueMart Team. All rights reserved.
 * @license http://www.gnu.org/copyleft/gpl.html GNU/GPL, see LICENSE.php
 * VirtueMart is free software. This version may have been modified pursuant
 * to the GNU General Public License, and as distributed it includes or
 * is derivative of works licensed under the GNU General Public License or
 * other free or open source software licenses.
 * @version $Id: user.php 3374 2011-05-26 09:44:02Z Milbo $
 */

// Check to ensure this file is included in Joomla!
defined('_JEXEC') or die('Restricted access');

// Hardcoded groupID of the Super Admin
define ('__SUPER_ADMIN_GID', 25);

// Load the model framework
jimport('joomla.application.component.model');
jimport('joomla.version');

if(!class_exists('VmModel'))require(JPATH_COMPONENT.DS.'helpers'.DS.'vmmodel.php');

/**
 * Model class for shop users
 *
 * @package	VirtueMart
 * @subpackage	User
 * @author	RickG
 * @author Max Milbers
 */
class VirtueMartModelUser extends JModel {

	/**
	 * Constructor for the user model.
	 *
	 * The user ID is read and detmimined if it is an array of ids or just one single id.
	 */
	function __construct(){

		parent::__construct();

		// Get the helpers we need here, This must be in the constructor, for the RC it would be good to place the
		// "require" nearest at possible to the code, which actually using it
		//		if(!class_exists('ShopperGroup')) require(JPATH_COMPONENT.DS.'helpers'.DS.'shoppergroup.php');
		if(!class_exists('user_info')) require(JPATH_COMPONENT.DS.'helpers'.DS.'user_info.php');

	}

	/**
	 * Resets the user id and data, you should avoid external use of this function
	 * I set it now to private.
	 *
	 * @author Max Milbers
	 */
	public function setId($cid){

		$user = JFactory::getUser();
		//anonymous sets to 0 for a new entry
		if(empty($user->id)){
			$this->setUserId(0);
			//			echo($this->_id,'Recogniced anonymous case');
		} else {
			//			$idArray = JRequest::getVar('cid',  null, '', 'array');
			//not anonymous, but no cid means already registered user edit own data
			if(empty($cid)){
				$this->setUserId($user->id);
				//				echo($user->id,'cid was null, therefore user->id is used');
			} else {
				if($cid != $user->id){
					if(!class_exists('Permissions')) require(JPATH_COMPONENT.DS.'helpers'.DS.'permissions.php');
					if(Permissions::getInstance()->check("admin,storeadmin")) {
						$this->setUserId($cid);
					} else {
						//						JError::raiseWarning(1,'Hacking attempt');
						$this->setUserId($user->id);
					}
				}else {
					$this->setUserId($user->id);
				}
			}
		}
	}

	private function setUserId($id){

		if($this->_id!=$id){
			$this->_id = (int)$id;
			$this->_data = null;
		}
	}
	/**
	 * Set the ID to the current user
	 */
	function setCurrent()
	{
		$_currentUser = JFactory::getUser();
		$this->setId($_currentUser->get('id'));
	}


	/**
	 * Gets the total number of users
	 *
	 * @return int Total number of users in the database
	 */
	function _getTotal()
	{
		if (empty($this->_total)) {
			$query = $this->_getListQuery();
			$this->_total = $this->_getListCount($query);
		}
		return $this->_total;
	}

	/**
	 * Load a single user_info record
	 *
	 * @param $_ui_id string Record id
	 * @return object Database object
	 */
	function _loadUserInfo($_ui_id)
	{
		$_data = $this->getTable('userinfos');
		$_data->load($_ui_id);
		return $_data;
	}

	/**
	 * Retrieve the detail record for the current $id if the data has not already been loaded.
	 */
	function getUser(){

		//This can cause trouble, when you save a new vendor in the form with apply. The new virtuemart_vendor_id is not set then
		//		if (empty($this->_data)) {

		$this->_data = new stdClass();

		if(empty($this->_db)) $this->_db = JFactory::getDBO();

		//			$this->_data = $this->getTable('vmusers');
		//			$this->_data->load((int)$this->_id);

		/* Add the virtuemart_shoppergroup_ids */
		//			$q = 'SELECT `virtuemart_shoppergroup_id` FROM #__virtuemart_vmuser_shoppergroups WHERE `virtuemart_user_id` = "'.$this->_id.'"';
		//			$this->_db->setQuery($q);
		//			$this->_data->shopper_groups = $this->_db->loadResultArray();

		// 			$xrefTable = $this->getTable('vmuser_shoppergroups');
		//			$this->_data->shopper_groups = $xrefTable->load($this->_id);


		$this->_data->JUser = JUser::getInstance($this->_id);

		$_ui = $this->_getList('SELECT `user_info_id` FROM `#__vm_user_info` WHERE `user_id` = "' . $this->_id.'"');

		$this->_data->userInfo = array ();

		for ($i = 0, $n = count($_ui); $i < $n; $i++) {
			$_ui_id = $_ui[$i]->user_info_id;
			$this->_data->userInfo[$_ui_id] = $this->_loadUserInfo($_ui_id);
			/*
			 * Hack by Oscar for Ticket #296 (redmine); user_is_vendor gets reset when a BT address is saved
			 * from the cart. I don't know is this is the only location, but it can be fixed by
			 * making sure the user_is_vendor field is in the BT dataset.
			 * I make this hack here, since I'm not sure if it causes problems on more locations.
			 * @TODO Find out is there's a more decvent solution. Maybe when the user_info table gets reorganised?
			 */
			if ($this->_data->userInfo[$_ui_id]->address_type == 'BT') {
				$this->_data->userInfo[$_ui_id]->user_is_vendor = $this->_data->user_is_vendor;
			}
			// End hack
			$this->_data->userInfo[$_ui_id]->email = $this->_data->JUser->email;
		}
		if($this->_data->user_is_vendor){
			if(!class_exists('VirtueMartModelVendor')) require(JPATH_COMPONENT.DS.'models'.DS.'vendor.php' );
			$vendorModel = new VirtueMartModelVendor();

			$vendorModel->setId($this->_data->virtuemart_vendor_id);
			$this->_data->vendor = $vendorModel->getVendor();
		}
		//		}


		return $this->_data;
	}

	/**
	 * This function just gets the post data and put the data if there is any to the cart
	 *
	 * @author Max Milbers
	 */
	public static function saveAddresToCart($address, $_type='BT'){
		if(!class_exists('VirtueMartCart')) require(JPATH_COMPONENT.DS.'helpers'.DS.'cart.php');
		$_cart = VirtueMartCart::getCart();
		
		$arr = array();
		foreach ( $address as $key => $data ) {
			$arr[$key] = $data;
		}
		
		$_cart->$_type = $arr;
		$_cart->setCartIntoSession();
	}



	/**
	 * Bind the post data to the JUser object and the VM tables, then saves it
	 * It is used to register new users
	 * This function can also change already registered users, this is important when a registered user changes his email within the checkout.
	 *
	 * @author Max Milbers
	 * @author Oscar van Eijk
	 * @return boolean True is the save was successful, false otherwise.
	 */
	function store($data=0)
	{
		//	JRequest::checkToken('get') or jexit( 'Invalid Token, while trying to save user' );


		/*
		 * To find out, if we have to register a new user, we take a look on the id of the usermodel object.
		 * The constructor sets automatically the right id.
		 * */
		$new = ($this->_id < 1);
		$user = JFactory::getUser($this->_id);
		$gid = $user->get('gid'); // Save original gid

		/*
		 * Before I used this "if($cart && !$new)"
		 * This construction is necessary, because this function is used to register a new JUser, so we need all the JUser data in $data.
		 * On the other hand this function is also used just for updating JUser data, like the email for the BT address. In this case the
		 * name, username, password and so on is already stored in the JUser and dont need to be entered again.
		 */
		if(empty ($data['email'])){
			$email = $user->get('email');
			if(!empty($email)){
				$data['email'] = $email;
			} else {
				$data['email'] = JRequest::getVar('email', '', 'post', 'email');
			}
		}

		//This is important, when a user changes his email address from the cart,
		//that means using view user layout edit_address (which is called from the cart)
		$user->set('email',$data['email']);

		if(empty ($data['name'])){
			$name = $user->get('name');
			if(!empty($name)){
				$data['name'] = $name;
			} else {
				$data['name'] = JRequest::getVar('name', '', 'post', 'name');
			}
		}

		if(empty ($data['username'])){
			$username = $user->get('username');
			if(!empty($username)){
				$data['username'] = $username;
			} else {
				$data['username'] = JRequest::getVar('username', '', 'post', 'username');
			}
		}

		if(empty ($data['password'])){
			$data['password'] = JRequest::getVar('password', '', 'post', 'string' ,JREQUEST_ALLOWRAW);
		}

		if(empty ($data['password2'])){
			$data['password2'] = JRequest::getVar('password2', '', 'post', 'string' ,JREQUEST_ALLOWRAW);
		}

		// Bind Joomla userdata
		if (!$user->bind($data)) {
			//develop
			$this->setError('user bind '.$user->getError());
			return false;
		}

		if($new){

			{
				// In this version we don't block and wait for email, allow the user buy quick
				jimport('joomla.user.helper');
				$user->set('activation', JUtility::getHash( JUserHelper::genRandomPassword()) );
				$user->set('block', '0');
				
				$authorize	=& JFactory::getACL();
				// Initialize new usertype setting
			//	$newUsertype = $usersConfig->get( 'new_usertype' );
				if (!$newUsertype) {
					$newUsertype = 'Registered';
				}

				// Set some initial user values
				$user->set('usertype', $newUsertype);
				
				$user->set('gid', $authorize->get_group_id( '', $newUsertype, 'ARO' ));
			}
			$date =& JFactory::getDate();
			$user->set('registerDate', $date->toMySQL());
			
		}

		$option = JRequest::getCmd( 'option');
		// If an exising superadmin gets a new group, make sure enough admins are left...
		if (!$new && $user->get('gid') != $gid && $gid == __SUPER_ADMIN_GID) {
			if ($this->getSuperAdminCount() <= 1) {
				return JError::raiseWarning('', JText::_('COM_VIRTUEMART_USER_ERR_ONLYSUPERADMIN'));
			}
		}

		// Save the JUser object
		if (!($msg = $user->save())) {
			return JError::raiseWarning('', JText::_( $user->getError()));
		}

		$newId = $user->get('id');
		$data['user_id'] = $newId;	//We need this in that case, because data is bound to table later
		$this->setId($newId);

		//Save the VM user stuff ************************** this should be a separate call
		if(!$this->saveUserData($data,$new)){
			//$this->setError(JText::_('COM_VIRTUEMART_NOT_ABLE_TO_SAVE_USER_DATA')  );
			$errors = $this->getErrors();
			if (count($errors)>0)
				return JError::raiseWarning('', $errors[0]);
		}

		// Everything went fine, set relevant message depending upon user activation state and display message
		if ($new) {
			if ( $useractivation == 1 ) {
				$message  = JText::_('COM_VIRTUEMART_REG_COMPLETE_ACTIVATE');
			} else {
				$message = JText::_('COM_VIRTUEMART_REG_COMPLETE');
			}
		} else {
			$message = JText::_('COM_VIRTUEMART_USER_DATA_STORED');	//TODO write right keystring
		}

		return $message;

	}

	/**
	 * This function is NOT for anonymous. Anonymous just get the information directly sent by email.
	 * This function saves the vm Userdata for registered JUsers.
	 * TODO, setting of shoppergroup isnt done
	 *
	 * TODO No reason not to use this function for new users, but it requires a Joomla <user> plugin
	 * that gets fired by the onAfterStoreUser. I'll built that (OvE)
	 *
	 * Notice:
	 * As long we do not have the silent registration, an anonymous does not get registered. It is enough to send the virtuemart_order_id
	 * with the email. The order is saved with all information in an extra table, so there is
	 * no need for a silent registration. We may think about if we actually need/want the feature silent registration
	 * The information of anonymous is stored in the order table and has nothing todo with the usermodel!
	 *
	 * @author Max Milbers
	 * @author Oscar van Eijk
	 * return boolean
	 */
	private function saveUserData($_data,$new){

		if(empty($this->_id)){
			echo 'This is a notice for developers, you used this function for an anonymous user, but it is only designed for already registered ones';
		}

		if(empty($_data['customer_number'])){
			//TODO here add plugin hoook for creating customer numbers.
			$_data['customer_number'] = md5($_data['username']);
		}
		
		if (empty($_data['first_name'])) {
			$_data['first_name'] = $_data['name'];
		}
		if (empty($_data['user_email'])) {
			$_data['user_email'] = $_data['email'];
		}
		
		if ( empty($_data['shopper_group_id'])) {
			//TODO get default from the db
			$shopperGourpsTbl = $this->getTable('shoppergroups');
			$shopperGourpsTbl->setDefaultId();
			$shopperGourpsTbl->load();
			if(!empty($shopperGourpsTbl->shopper_group_id))
				$_data['shopper_group_id'] = $shopperGourpsTbl->shopper_group_id;
			else 
				$_data['shopper_group_id'] = 5; // lol :D 
			
		}
		
		if ( empty ($_data['vendor_id'])) {
			//TODO get default from the db
			$_data['vendor_id']=1;// ok this isn't that bad since 1 is default vendor allways
		}
		
		if(!empty($_data['shopper_group_id'])){
				// Bind the form fields to the auth_user_group table
				$shoppergroupData = array('user_id'=>$this->_id,'vendor_id'=>$_data['vendor_id'], 'shopper_group_id'=>$_data['shopper_group_id']);
				$user_shoppergroups_table = $this->getTable('vmuser_shoppergroups');

				$shoppergroupData = $user_shoppergroups_table -> bindChecknStore($shoppergroupData);
				$errors = $user_shoppergroups_table->getErrors();
				foreach($errors as $error){
					$this->setError($error);
				}
		}

		if (!user_info::storeAddress($_data, 'userinfos', $new)) {
			//$this->setError(Jtext::_('COM_VIRTUEMART_NOT_ABLE_TO_SAVE_USERINFO_DATA'));
			return false;
		}

		return true;
	}


	/**
	 * This uses the shopfunctionsF::renderAndSendVmMail function, which uses a controller and task to render the content
	 * and sents it then.
	 *
	 * @deprecated: Sends a standard registration email.
	 *
	 * @author Oscar van Eijk
	 * @author Max Milbers
	 * @author Christopher Roussel
	 */
	function sendRegistrationEmail($user){
		if(!class_exists('shopFunctionsF')) require(JPATH_VM_SITE.DS.'helpers'.DS.'shopfunctionsf.php');
		$vars = array('user' => $user);

		// Send registration confirmation mail
		$password = JRequest::getString('password', '', 'post', JREQUEST_ALLOWRAW);
		$password = preg_replace('/[\x00-\x1F\x7F]/', '', $password); //Disallow control chars in the email
		$vars['password'] = $password;

		// If user activation is turned on, we need to set the activation information
		$usersConfig = JComponentHelper::getParams('com_users');
		$useractivation = $usersConfig->get('useractivation');
		if ($useractivation == '1') {
			jimport('joomla.user.helper');
			$activationLink = 'index.php?option=com_user&task=activate&activation='.$user->get('activation');
			$vars['activationLink'] = $activationLink;
		}

		shopFunctionsF::renderMail('user', $user->get('email'), $vars);
	}


	/**
	 * Retrieve a list of users from the database.
	 *
	 * @return object List of user objects
	 */
	function getUserList()
	{
		$query = $this->_getListQuery();
		$this->_data = $this->_getList($query, $this->getState('limitstart'), $this->getState('limit'));

		return $this->_data;
	}

	/**
	 * Retrieve a list of addresses for a user
	 *
	 *  @param $_uid int User ID
	 *  @param $_type string, addess- type, ST (ShipTo, default) or BT (BillTo)
	 */
	function getUserAddressList($_uid = 0, $_type = 'ST')
	{
		$_q = 'SELECT * '
		. ' FROM #__virtuemart_userinfos '
		. " WHERE virtuemart_user_id='" . (($_uid==0)?$this->_id:$_uid) . "' "
		. " AND address_type='$_type'";
		return ($this->_getList($_q));
	}

	/**
	 * Retrieve a single address for a user
	 *
	 *  @param $_uid int User ID
	 *  @param $_virtuemart_userinfo_id string Optional User Info ID
	 *  @param $_type string, addess- type, ST (ShipTo, default) or BT (BillTo). Empty string to ignore
	 */
	function getUserAddress($_uid = 0, $_virtuemart_userinfo_id = -1, $_type = 'BT')
	{
		$_q = 'SELECT * '
		. ' FROM #__vm_user_info '
		. " WHERE user_id='" . (($_uid==0)?$this->_id:$_uid) . "' ";
		if ($_type !== '') {
			$_q .= " AND address_type='$_type'";
		}
		if ($_virtuemart_userinfo_id !== -1) {
			$_q .= " AND user_info_id='$_virtuemart_userinfo_id'";
		}
		return ($this->_getList($_q));
	}

	/**
	 * Retrieves the Customer Number of the user specified by ID
	 *
	 * @param int $_id User ID
	 * @return string Customer Number
	 */
	function getCustomerNumberById($_id = 0)
	{
		$_q = "SELECT `customer_number` FROM `#__virtuemart_vmusers` "
		."WHERE `virtuemart_user_id`='" . (($_id==0)?$this->_id:$_id) . "' ";
		$_r = $this->_getList($_q);
		if(!empty($_r[0])){
			return $_r[0]->customer_number;
		}else {
			return 0;
		}

	}

	/**
	 * If a filter was set, get the SQL WHERE clase
	 *
	 * @return string text to add to the SQL statement
	 */
	function _getFilter()
	{
		if (JRequest::getVar('search', false)) {
			$_where = ' WHERE `name` LIKE ' .$this->_db->Quote('%'.JRequest::getVar('search').'%')
			. ' OR `username` LIKE ' .$this->_db->Quote('%'.JRequest::getVar('search').'%');
			return ($_where);
		}
		return ('');
	}

	/**
	 * Get the SQL Ordering statement
	 *
	 * @return string text to add to the SQL statement
	 */
	function _getOrdering()
	{
		$option = JRequest::getCmd( 'option');
		$mainframe = JFactory::getApplication() ;

		$filter_order_Dir = $mainframe->getUserStateFromRequest( $option.JRequest::getVar('view').'filter_order_Dir', 'filter_order_Dir', 'asc', 'word' );
		$filter_order     = $mainframe->getUserStateFromRequest( $option.JRequest::getVar('view').'filter_order', 'filter_order', 'id', 'cmd' );
		if (!$filter_order) return '';
		// FIXME this is a dirty hack since we don't have an ordering field yet...
		if ($filter_order == 'ordering') $filter_order = 'id';
		return (' ORDER BY '.$filter_order.' '.$filter_order_Dir);
	}

	/**
	 * Build the query to list all Users
	 *
	 * @return string SQL query statement
	 */
	function _getListQuery (){

		// Used tables #__virtuemart_vmusers, #__virtuemart_userinfos, #__vm_user_perm_groups, #__virtuemart_vmuser_shoppergroups, #__virtuemart_vendors
		$query = 'SELECT DISTINCT ju.id AS id '
		. ', ju.name AS name'
		. ', ju.username AS username '
		. ', vmu.user_is_vendor AS is_vendor'
		. ', vmu.perms AS perms'
		. ', ju.usertype AS usertype'
		. ", IFNULL(sg.shopper_group_name, '') AS shopper_group_name "
		. 'FROM #__users AS ju '
		. 'LEFT JOIN #__virtuemart_vmusers AS vmu ON ju.id = vmu.virtuemart_user_id '
		. 'LEFT JOIN #__virtuemart_vmuser_shoppergroups AS vx ON ju.id = vx.virtuemart_user_id '
		. 'LEFT JOIN #__virtuemart_shoppergroups AS sg ON vx.virtuemart_shoppergroup_id = sg.virtuemart_shoppergroup_id ';
		$query .= $this->_getFilter();
		$query .= $this->_getOrdering();

		return ($query);
	}

	/**
	 * Take a list of userIds and check if they all have a record in #__virtuemart_userinfos
	 *
	 * @author Oscar van Eijk
	 * @param $_ids Array with userIds to check (uId, uId, ...)
	 * @return array with invalid users (userId => userName, ...)
	 */
	function validateUsers ($_ids = array())
	{
		if (count($_ids) == 0) {
			return array();
		}
		$_missing = $this->_getList('SELECT j.username AS uname '
		. ',      j.id       AS uid '
		. 'FROM `#__users` j '
		. 'WHERE j.id IN (' . join(',', $_ids) . ') '
		. 'AND NOT EXISTS ('
		. 'SELECT virtuemart_user_id FROM `#__virtuemart_userinfos` v '
		. 'WHERE v.virtuemart_user_id = j.id'
		. ')'
		);
		$_missingUsers = array();
		foreach ($_missing as $_m) {
			$_missingUsers[$_m->uid] = $_m->uname;
		}
		return $_missingUsers;
	}

	/**
	 * Return a list of Joomla ACL groups.
	 *
	 * The returned object list includes a group anme and a group name with spaces
	 * prepended to the name for displaying an indented tree.
	 *
	 * @author RickG
	 * @return ObjectList List of acl group objects.
	 */
	function getAclGroupIndentedTree()
	{

		//TODO check this out
		if (VmConfig::isJ15()) {
			$name = 'name';
			$as = '` AS `title`';
			$table = '#__core_acl_aro_groups';
			$and = 'AND `parent`.`lft` > 2 ';
	 	}
	 	else {
			$name = 'title';
			$as = '`';
			$table = '#__usergroups';
			$and = '';
	 	}

	 	$query = 'SELECT `node`.`' . $name . $as . ', CONCAT(REPEAT("&nbsp;&nbsp;&nbsp;", (COUNT(`parent`.`' . $name . '`) - 1)), `node`.`' . $name . '`) AS `text` ';
	 	$query .= 'FROM `' . $table . '` AS node, `' . $table . '` AS parent ';
	 	$query .= 'WHERE `node`.`lft` BETWEEN `parent`.`lft` AND `parent`.`rgt` ';
	 	$query .= $and;
	 	$query .= 'GROUP BY `node`.`' . $name . '` ';
	 	$query .= 'ORDER BY `node`.`lft`';

	 	$this->_db->setQuery($query);
	 	return $this->_db->loadObjectList();
	 }
}


//No Closing tag
